﻿<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">

<head>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
<title>Entity Framework Core</title>
<link type="text/css" rel="stylesheet" href="bootstrap.min.css" />
</head>

<body>

<div class="document-contents">

<h3>Introduction - 介绍</h3>
<p>
<a href="https://www.nuget.org/packages/Abp.EntityFrameworkCore" target="_blank">
Abp.EntityFrameworkCore</a> nuget package is used to integrate to Entity 
Framework (EF) Core ORM framework. After installing this package, we should also add 
a <a href="Module-System.html">DependsOn</a> attribute for <strong>
AbpEntityFrameworkCoreModule</strong>.</p>
<p class="translation">
	<a href="https://www.nuget.org/packages/Abp.EntityFrameworkCore" target="_blank">Abp.EntityFrameworkCore</a> nuget 包被用于整合 Entity Framework (EF) Core ORM 框架。
	在安装这个包后，我们还应该为 AbpEntityFrameworkCoreModule 添加 <a href="/Module-System.html">DependsOn</a> 特性。
</p>
	<h3>DbContext</h3>
	<p>EF Core requires to define a class derived from DbContext. In ABP, we 
	should derive from <strong>AbpDbContext</strong> as shown below:</p>
	<p class="translation">
		EF Core 需要定义一个派生自 DbContext 的类。
		在 ABP 中，我们应该从 <strong>AbpDbContext</strong> 派生，如下所示：
	</p>
	<pre lang="cs">public class MyDbContext : <strong>AbpDbContext</strong>
{
    public DbSet&lt;Product&gt; Products { get; set; }

    public MyDbContext(<strong>DbContextOptions&lt;MyDbContext&gt; options</strong>)
        : base(options)
    {
    }
}</pre>
	<p>Constructor should get a <strong>DbContextOptions&lt;T&gt;</strong> as shown 
	above. Parameter name must be <strong>options</strong>. It's not possible to change it because ABP provides it as anonymous object parameter.</p>
	<p class="translation">
		如上所示，构造函数应该获取一个 <strong>DbContextOptions&lt;T&gt;</strong>。
		参数名称必须是 <strong>options</strong>。
		这是无法更改的，因为 ABP 是以匿名对象参数的方式提供它。
	</p>
	<h3>Configuration - 配置</h3>
	<h4>In Startup Class - 在 Startup 类中</h4>
	<p>Use <strong>AddAbpDbContext</strong> method on service collection in
	<strong>ConfigureServices</strong> method as shown below:</p>
	<p class="translation">
		在 <strong>ConfigureServices</strong> 方法中的服务集合上使用 <strong>AddAbpDbContext</strong> 方法，如下所示：
	</p>
	<pre lang="cs">services.<strong>AddAbpDbContext</strong>&lt;MyDbContext&gt;(options =&gt;
{
    options.DbContextOptions.UseSqlServer(options.ConnectionString);
});</pre>
	<p>For non web projects, we will not have a Startup class. In that case, we 
	can use
<strong>        Configuration.Modules.AbpEfCore().AddDbContext</strong> method 
	in our <a href="/Module-System.html">module </a>class to configure DbContext, as shown below:</p>
	<p class="translation">
		对于非 Web 项目，我们没有 Startup 类。
		在这种情况下，我们可以使用在<a href="/Module-System.html">模块</a>类中的 <strong>Configuration.Modules.AbpEfCore().AddDbContext</strong> 方法来配置 DbContext，如下所示：
	</p>
	<pre lang="cs">Configuration.Modules.AbpEfCore().<strong>AddDbContext</strong>&lt;MyDbContext&gt;(options =&gt;
{
    options.DbContextOptions.UseSqlServer(options.ConnectionString);
});</pre>

			<p>We used given connection string and used Sql Server as database provider. <strong>options.ConnectionString</strong> 
	is the <strong>default connection string</strong> (see next section) 
	normally. But ABP uses IConnectionStringResolver to determine it. So, 
	this behaviour can be changed and connection string can be determined 
	dynamically. The action passed to AddDbContext is called whenever a DbContext instance 
	will be created. So, you also have a chance to return different connection 
	string conditionally.</p>
	<p class="translation">
		我们使用给定的链接字符串以及 SQL Server 作为数据库提供者。<strong>options.ConnectionString</strong> 通常是<strong>默认链接字符串</strong>（见下一节）。
		但 ABP 使用 IConnectionStringResolver 来决定它。因此，这种行为是可以被改变的并且链接字符串可以被动态决定。
		每当 DbContext 被创建，AddDbContext 被调用时 action 将被传递给它。
		因此，你有机会根据条件返回不同的链接字符串。
	</p>
	<p>So, where to set default connection string?</p>
	<p class="translation">那么，在哪设置默认链接字符串？</p>
	<h4>In Module PreInitialize - 在模块 PreInitialize 中</h4>
	<p>You can do it in PreInitialize of your module as shown below:</p>
	<p class="translation">你可以在模块的 PreInitialize 中进行，如下所示：</p>
	<pre lang="cs">public class MyEfCoreAppModule : AbpModule
{
    public override void PreInitialize()
    {
<strong>        Configuration.DefaultNameOrConnectionString = GetConnectionString(&quot;Default&quot;);</strong>
        ...
    }
}</pre>
	<p>So, you can define GetConnectionString method simply returns the 
	connection string from a configuration file (generally from 
	appsettings.json). </p>
	<p class="translation">
		所以，你可定义 GetConnectionString 方法，它简单的从配置文件中返回链接字符串（通常从 appsettings.json）。
	</p>

			<h3 id="DocRepositories">Repositories - 仓库</h3>
			<p>Repositories are used to abstract data access from higher layers. 
			See <a href="/Repositories.html">repository documentation</a> for 
			more.&nbsp;</p>
			<p class="translation">
				仓库是被用来从更高层次抽象数据访问的。更多信息参阅<a href="/Repositories.html">仓库文档</a>。
			</p>
			<h4 id="DocImplRepositories">Default Repositories - 默认仓库</h4>
			<p>
				<a href="http://www.nuget.org/packages/Abp.EntityFrameworkCore" target="_blank">Abp.EntityFrameworkCore</a> 
			implements default repositories for all entities defined in your 
			DbContext. You don't have to create repository classes to use 
predefined repository methods. Example:</p>
			<p class="translation">
				<a href="http://www.nuget.org/packages/Abp.EntityFrameworkCore" target="_blank">Abp.EntityFrameworkCore</a> 为所有定义在 DbContext 中的所有实体实现了默认仓库。
				你不用创建仓库类，可以使用预定义的仓库方法。例如：
			</p>
			<pre lang="cs">public class PersonAppService : IPersonAppService
{
    private readonly <strong>IRepository&lt;Person&gt; _personRepository</strong>;

    public PersonAppService(<strong>IRepository&lt;Person&gt; personRepository</strong>)
    {
        _personRepository = personRepository;
    }

    public void CreatePerson(CreatePersonInput input)
    {        
        person = new Person { Name = input.Name, EmailAddress = input.EmailAddress };

        <strong>_personRepository.Insert(person);
</strong>    }
}</pre>

			<p>PersonAppService contructor-injects <strong>IRepository&lt;Person&gt;</strong> and 
uses the <strong>Insert</strong> method. In this way, you can easily inject
				<strong>IRepository&lt;TEntity&gt;</strong> (or 
			IRepository&lt;TEntity, TPrimaryKey&gt;) and use predefined methods.</p>
			<p class="translation">
				PersonAppService 以构造函数注入了 <strong>IRepository&lt;Person&gt;</strong>，并使用了 <strong>Insert</strong> 方法。
				通过这种方法，你可以很容易的注入 <strong>IRepository&lt;TEntity&gt;</strong>（或 IRepository&lt;TEntity, TPrimaryKey&gt;），并使用其预定义的方法。
			</p>

			<h4 id="DocCustomRepositoryMethods">Custom Repositories - 自定义仓库</h4>
			<p>If standard repository methods are not sufficient, you can create 
			custom repository classes for your entities.</p>
			<p class="translation">
				如果标准仓库方法不够使用，你可以为你的实体创建自定义仓库类。
			</p>
			<h5 id="DocAppBaseRepo">Application Specific Base Repository Class 应用程序特定仓库基类</h5>

			<p>ASP.NET Boilerplate provides a base class <strong>EfCoreRepositoryBase</strong> 
to implement repositories easily. To implement <strong>IRepository</strong> interface, you can 
just derive your repository from this class. But it's better to create your own 
base class that extens EfRepositoryBase. Thus, you can add shared/common methods 
to your repositories easily. An example base class all for repositories of a <em>SimpleTaskSystem</em> application:</p>
			<p class="translation">
				ASP.NET Boilerplate 提供了基类 <strong>EfCoreRepositoryBase</strong> 来更容易的实现仓库。
				要实现 <strong>IRepository</strong> 接口，你可以从这个类派生你的仓库。
				但是最好是通过扩展 EfRepositoryBase 来创建自己的基类。
				如此，你可以更容易的添加共享/公共方法到你的仓库。
				<em>SimpleTaskSystem</em> 应用程序中所有仓库的基类示例：
			</p>
			<pre lang="cs">//Base class for all repositories in my application
public class SimpleTaskSystemRepositoryBase&lt;TEntity, TPrimaryKey&gt; : <strong>EfCoreRepositoryBase&lt;SimpleTaskSystemDbContext, TEntity, TPrimaryKey&gt;</strong>
    where TEntity : class, IEntity&lt;TPrimaryKey&gt;
{
    public SimpleTaskSystemRepositoryBase(<strong>IDbContextProvider&lt;SimpleTaskSystemDbContext&gt; dbContextProvider</strong>)
        : base(dbContextProvider)
    {
    }

    //add common methods for all repositories
}

//A shortcut for entities those have integer Id
public class SimpleTaskSystemRepositoryBase&lt;TEntity&gt; : <strong>SimpleTaskSystemRepositoryBase&lt;TEntity, int&gt;</strong>
    where TEntity : class, IEntity&lt;int&gt;
{
    public SimpleTaskSystemRepositoryBase(<strong>IDbContextProvider&lt;SimpleTaskSystemDbContext&gt; dbContextProvider</strong>)
        : base(dbContextProvider)
    {
    }

    //do not add any method here, add to the class above (because this class inherits it)
}</pre>
			<p>Notice that we're inheriting from EfCoreRepositoryBase&lt;<strong>SimpleTaskSystemDbContext</strong>, TEntity, TPrimaryKey&gt;. 
This declares to ASP.NET Boilerplate to use SimpleTaskSystemDbContext in our 
repositories.</p>
			<p class="translation">
				注意，我们是从 EfCoreRepositoryBase&lt;<strong>SimpleTaskSystemDbContext</strong>, TEntity, TPrimaryKey&gt; 继承的。
				这声明了 ASP.NET Boilerplate 在我们的仓库中使用 SimpleTaskSystemDbContext。
			</p>
			<p>By default, all repositories for your given DbContext 
			(SimpleTaskSystemDbContext in this example) is implemented using 
			EfCoreRepositoryBase. You can replace it to your own repository base 
			repository class by adding <strong>AutoRepositoryTypes</strong> 
			attribute to your DbContext as shown below:</p>
			<p class="translation">
				默认情况下，对于给定 DbContext（这个示例中是 SimpleTaskSystemDbContext）的所有仓库都是使用 EfCoreRepositoryBase 实现的。
				你可以通过添加 <strong>AutoRepositoryTypes</strong> 特性到你的 DbContext 中来替代你自身仓库的仓库基类。如下所示：
			</p>
<pre lang="cs"><strong>[AutoRepositoryTypes(
    typeof(IRepository&lt;&gt;),
    typeof(IRepository&lt;,&gt;),
    typeof(SimpleTaskSystemEfRepositoryBase&lt;&gt;),
    typeof(SimpleTaskSystemEfRepositoryBase&lt;,&gt;)
)]</strong>
public class SimpleTaskSystemDbContext : AbpDbContext
{
    ...
}</pre>
			<h5>Custom Repository Example - 自定义仓库示例</h5>

			<p>To implement a custom repository, just derive from your application specific 
base repository class we created above.</p>
			<p class="translation">
				为了实现一个自定义仓库，你只需要从上述创建的应用程序特定仓库基类中派生。
			</p>
			<p>Assume that we have a Task entity that can be assigned to a Person (entity) 
and a Task has a State (new, assigned, completed... and so on). We may need to 
write a custom method to get list of Tasks with some conditions and with 
AssisgnedPerson property pre-fetched (included) in a single database query. See the example 
code:</p>
			<p class="translation">
				假设我们挺拥有一个 Task 实体，可以指派到 Person（实体）并且 Task 拥有一个 State（新建，分派，完成等等...）。
				我们可能需要编写一个自定义方法在单个数据库查询中根据条件获取任务的列表，使用 AssisgnedPerson 属性预取（包含）。
				见示例代码：
			</p>
			<pre>public interface ITaskRepository : <strong>IRepository&lt;Task, long&gt;
</strong>{
    List&lt;Task&gt; GetAllWithPeople(int? assignedPersonId, TaskState? state);
}

public class TaskRepository : <strong>SimpleTaskSystemRepositoryBase&lt;Task, long&gt;, ITaskRepository
</strong>{
    public TaskRepository(IDbContextProvider&lt;SimpleTaskSystemDbContext&gt; dbContextProvider)
        : base(dbContextProvider)
    {
    }

    public List&lt;Task&gt; GetAllWithPeople(int? assignedPersonId, TaskState? state)
    {
        var query = GetAll();

        if (assignedPersonId.HasValue)
        {
            query = query.Where(task =&gt; task.AssignedPerson.Id == assignedPersonId.Value);
        }

        if (state.HasValue)
        {
            query = query.Where(task =&gt; task.State == state);
        }

        return query
            .OrderByDescending(task =&gt; task.CreationTime)
            .Include(task =&gt; task.AssignedPerson)
            .ToList();
    }
}</pre>
			<p>
				<strong class="auto-style2">We first defined </strong>ITaskRepository and 
then implemented it.<strong> GetAll()</strong> returns <strong>IQueryable&lt;Task&gt;</strong>, then we can add some
				<strong>Where</strong> filters using given parameters. Finally we can call
				<strong>ToList()</strong> to get list of 
Tasks.</p>
			<p class="translation">
				<strong class="auto-style2">我们首先定义</strong>了 ITaskRepository，然后实现了它。
				<strong>GetAll()</strong> 返回了 <strong>IQueryable&lt;Task&gt;</strong>，然后我们可以使用给定的参数添加一些 <strong>Where</strong> 过滤器。
				最后，我们可以调用 <strong>ToList()</strong> 得到任务的列表。
			</p>
			<p>You can also use <strong>Context </strong>object in repository methods to 
reach to your DbContext and directly use Entity Framework APIs.&nbsp;</p>
			<p class="translation">
				你还可以在仓库方法中使用 <strong>Context</strong> 对象来得到 DbContext，并直接使用 Entity Framework API。
			</p>
	<p><strong>Note</strong>: Define the custom repository <strong>interface
	</strong>in the <strong>domain/core</strong> layer, <strong>implement
	</strong>it in the <strong>EntityFrameworkCore </strong>project for layered 
	applications. Thus, you can inject the interface from any project without 
	referencing to EF Core.</p>
	<p class="translation">
		<strong>注</strong>：对于分层应用程序，在<strong>领域/核心层</strong>定义自定义仓库<strong>接口</strong>，在 <strong>EntityFrameworkCore</strong> 项目中<strong>实现</strong>它。
		因此，你可以从任务项目注入接口而不用引用 EF Core。
	</p>
	<h5>Replacing Default Repositories - 替代默认仓库</h5>
	<p>Even you have created a TaskRepository as shown above, any class can 
	still <a href="Dependency-Injection.html">inject</a> IRepository&lt;Task, long&gt; 
	and use it. That's not a problem in most cases. But, what if you <strong>
	overrided</strong> a base method in your custom repository? Say that you 
	have overrided Delete method in your custom repository to add a custom 
	behaviour on delete. If a class injects IRepository&lt;Task, long&gt; and use the 
	default repository to Delete a task, your custom behaviour will not work. To 
	overcome this issue, you can replace your custom repository implementation 
	with the default one like shown below:</p>
	<p class="translation">
		即使你创建了如上所述的 TaskRepository，但在类中仍然可以<a href="Dependency-Injection.html">注入</a> IRepository&lt;Task, long&gt; 并使用它。
		这在多数情况下都不是问题。但是，如果你在自定义仓库中<strong>重写</strong>了一个基类方法会怎样？
		例如，你在自定义仓库中重写了 Delete 方法，添加了自定义的行为。
		如果一个类注入了 IRepository&lt;Task, long&gt;，并使用默认仓库来删除任务，你的自定义行为将不能工作。
		为了克服这个问题，你可以像下面一样用你的自定义仓库替代默认实现。
	</p>
	<pre lang="cs">Configuration.ReplaceService&lt;<strong>IRepository&lt;Task, Guid&gt;</strong>&gt;(() =&gt;
{
    IocManager.IocContainer.Register(
        Component.For&lt;<strong>IRepository&lt;Task, Guid&gt;, ITaskRepository, TaskRepository</strong>&gt;()
            .ImplementedBy&lt;<strong>TaskRepository</strong>&gt;()
            .<strong>LifestyleTransient</strong>()
    );
});</pre>
	<p>We registered TaskRepository for IRepository&lt;Task, Guid&gt;, 
	ITaskRepository and TaskRepository. So, any one of these can be injected to 
	use the TaskRepository.</p>
	<p class="translation">
		我们为 IRepository&lt;Task, Guid&gt;, ITaskRepository 和 TaskRepository 注册了 TaskRepository。
		因此，它们中的任何一个被注册都会使用 TaskRepository。
	</p>
			<h4>Repository Best Practices - 仓库最佳实践</h4>
			<ul>
				<li><strong>Use default repositories</strong> wherever it's 
				possible. You can use default repository even you have a custom 
				repository for an entity (if you will use standard repository 
				methods).</li>
				<p class="translation">
					尽可能<strong>使用默认仓库</strong>。即使你的一个实体拥有一个自定义仓库，你也可以使用默认仓库（如果你将使用标准仓库方法）。
				</p>
				<li>Always create <strong>repository base class</strong> for 
				your application for custom repositories, as defined above.</li>
				<p class="translation">
					如上所述，始终为你的应用程序的自定义仓库创建<strong>仓库基类</strong>。
				</p>
				<li>Define <strong>interfaces </strong>for your custom 
				repositories in <strong>domain layer</strong> (.Core project in 
				startup template), custom repository <strong>classes </strong>in
				<strong>.EntityFrameworkCore</strong> project if you want to 
				abstract EF Core from your domain/application.</li>
				<p class="translation">
					如果你想从你的领域/应用程序抽象 EF Core，在<strong>领域层</strong>（启动模板中的 .Core 项目）中定义自定义仓库的<strong>接口</strong>，
					在 <strong>.EntityFrameworkCore</strong> 项目中实现自定义仓库<strong>类</strong>。
				</p>
			</ul>

</div>

</body>

</html>
